package org.umss.fixturebuilder.controller;

import com.googlecode.objectify.Key;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.umss.fixturebuilder.builder.BuilderManager;
import org.umss.fixturebuilder.builder.PhaseBuilder;
import org.umss.fixturebuilder.builder.PhaseBuilderException;
import org.umss.fixturebuilder.builder.PhaseBuilderFactory;
import org.umss.fixturebuilder.builder.data.DataSource;
import org.umss.fixturebuilder.builder.data.DataSourceException;
import org.umss.fixturebuilder.builder.data.DataSourceFactory;
import static org.umss.fixturebuilder.builder.data.OfyService.ofy;
import org.umss.fixturebuilder.builder.model.Game;
import org.umss.fixturebuilder.builder.model.Phase;
import org.umss.fixturebuilder.builder.model.Round;
import org.umss.fixturebuilder.builder.model.Team;
import org.umss.fixturebuilder.model.appengine.ChampionshipModel;
import org.umss.fixturebuilder.model.appengine.GameModel;
import org.umss.fixturebuilder.model.appengine.PhaseModel;
import static org.umss.fixturebuilder.model.appengine.PhaseType.ALL_AGAINST_ALL;
import static org.umss.fixturebuilder.model.appengine.PhaseType.DIRECT_ELIMINATION;
import org.umss.fixturebuilder.model.appengine.RoundModel;
import org.umss.fixturebuilder.model.appengine.TeamModel;
import org.umss.fixturebuilder.model.json.JsonNewPhase;
import org.umss.fixturebuilder.model.json.JsonPhaseList;

/**
 *
 * @author Daniela Meneses
 */
public class PhaseController {

    public static Phase create(JsonNewPhase phase) {
        Phase generatedFixture = null;
        if (ChampionshipController.existWithoutPhases(phase.getChampionshipId())) {
            try {
                PhaseBuilder phaseBuilder;
                switch (phase.getType()) {
                    case ALL_AGAINST_ALL:
                        phaseBuilder = PhaseBuilderFactory.createAllAgainstAllPhaseBuilder();
                        break;
                    case DIRECT_ELIMINATION:
                        phaseBuilder = PhaseBuilderFactory.createDirectEliminationPhaseBuilder();
                        break;
                    case GROUPS:
                        phaseBuilder = PhaseBuilderFactory.createGroupPhaseBuilder(phase.getTeamPerGroup());
                        break;
                    default:
                        return null;
                }

                DataSource dbDataSource = DataSourceFactory.getDBDataSource(phase.getChampionshipId());
                BuilderManager manager = new BuilderManager(dbDataSource, phaseBuilder);
                generatedFixture = manager.generateFixture();
                savePhase(generatedFixture, phase.getChampionshipId());
            } catch (DataSourceException ex) {
                Logger.getLogger(PhaseController.class.getName()).log(Level.SEVERE, null, ex);
                return null;
            } catch (PhaseBuilderException ex) {
                Logger.getLogger(PhaseController.class.getName()).log(Level.SEVERE, null, ex);
                return null;
            }
        }

        return generatedFixture;
    }

    private static void savePhase(Phase generatedFixture, String championship) {
        Key<ChampionshipModel> championshipKey = Key.create(ChampionshipModel.class, championship);
        Key<PhaseModel> phaseKey = ofy().save().entity(new PhaseModel(championshipKey, generatedFixture.getType())).now();
        List<Round> rounds = generatedFixture.getRounds();
        for (Round round : rounds) {
            saveRound(phaseKey, round, championshipKey);
        }
    }

    private static void saveRound(Key<PhaseModel> phaseKey, Round round, Key<ChampionshipModel> champKey) {
        Key<RoundModel> roundKey = ofy().save().entity(new RoundModel(phaseKey)).now();
        List<Game> games = round.getGames();
        for (Game game : games) {
            saveGame(roundKey, game, champKey);
        }
    }

    private static void saveGame(Key<RoundModel> roundKey, Game game, Key<ChampionshipModel> champKey) {
        Team local = game.getLocal();
        Team visitor = game.getVisitor();
        if (game.getLocal() != null) {
            Key<TeamModel> localKey = Key.create(champKey, TeamModel.class, local.getId());
            Key<TeamModel> visitorKey = Key.create(champKey, TeamModel.class, visitor.getId());
            ofy().save().entity(new GameModel(roundKey, localKey, visitorKey));
        } else {
            ofy().save().entity(new GameModel(roundKey));
        }
    }

    public static JsonPhaseList getAll(String championshipId) {
        Key<ChampionshipModel> championshipKey = Key.create(ChampionshipModel.class, championshipId);
        List<PhaseModel> list = ofy().load().type(PhaseModel.class).ancestor(championshipKey).list();
        List<Phase> phases = new ArrayList<Phase>(list.size());

        for (PhaseModel phaseModel : list) {
            phases.add(createPhase(phaseModel, championshipKey));
        }

        return new JsonPhaseList(phases);
    }

    private static Phase createPhase(PhaseModel phaseModel, Key<ChampionshipModel> championshipKey) {
        Key<PhaseModel> phaseKey = Key.create(championshipKey, PhaseModel.class, phaseModel.getId());
        List<RoundModel> list = ofy().load().type(RoundModel.class).ancestor(phaseKey).list();
        List<Round> rounds = new ArrayList<Round>(list.size());

        for (RoundModel roundModel : list) {
            rounds.add(createRound(roundModel, championshipKey, phaseKey));
        }

        return new Phase(rounds, phaseModel.getType());
    }

    private static Round createRound(RoundModel roundModel, Key<ChampionshipModel> championshipKey, Key<PhaseModel> phaseKey) {
        Key<RoundModel> roundKey = Key.create(phaseKey, RoundModel.class, roundModel.getId());
        List<GameModel> gameModelList = ofy().load().type(GameModel.class).ancestor(roundKey).list();
        List<Key<GameModel>> gameModelKeys = ofy().load().type(GameModel.class).ancestor(roundKey).keys().list();
        List<Game> games = new ArrayList<Game>(gameModelList.size());

        for (int index = 0; index < gameModelList.size(); index++) {
            Game createdGame = createGame(gameModelList.get(index), championshipKey);
            createdGame.setId(gameModelKeys.get(index).getString());
            games.add(createdGame);
        }

        return new Round(games);
    }

    private static Game createGame(GameModel gameModel, Key<ChampionshipModel> championshipKey) {
        if (gameModel.getLocal() == null) {
            return new Game(null, null, gameModel.getLocalScore(), gameModel.getVisitorScore(), gameModel.isTerminated());
        }

        TeamModel localModel = ofy().load().type(TeamModel.class).ancestor(championshipKey).filterKey(gameModel.getLocal()).first().now();
        TeamModel visitorModel = ofy().load().type(TeamModel.class).ancestor(championshipKey).filterKey(gameModel.getVisitor()).first().now();
        Team local = new Team(localModel.getName());
        Team visitor = new Team(visitorModel.getName());
        return new Game(local, visitor, gameModel.getLocalScore(), gameModel.getVisitorScore(), gameModel.isTerminated());
    }
}
